﻿using CSScripting;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Logging;
using Spring.Util;
using Sys.Workflow.Bpmn.Constants;
using Sys.Workflow.Bpmn.Models;
using Sys.Workflow.Engine;
using Sys.Workflow.Engine.Delegate;
using Sys.Workflow.Engine.Delegate.Events;
using Sys.Workflow.Engine.Delegate.Events.Impl;
using Sys.Workflow.Engine.dynamic;
using Sys.Workflow.Engine.History;
using Sys.Workflow.Engine.impl.interceptor;
using Sys.Workflow.Engine.impl.runtime;
using Sys.Workflow.Engine.impl.util;
using Sys.Workflow.Engine.Impl.Bpmn.Behavior;
using Sys.Workflow.Engine.Impl.Bpmn.Diagram;
using Sys.Workflow.Engine.Impl.Cfg;
using Sys.Workflow.Engine.Impl.Delegate;
using Sys.Workflow.Engine.Impl.EL;
using Sys.Workflow.Engine.Impl.Interceptor;
using Sys.Workflow.Engine.Impl.JobExecutors;
using Sys.Workflow.Engine.Impl.Persistence.Deploies;
using Sys.Workflow.Engine.Impl.Persistence.Entity;
using Sys.Workflow.Engine.Impl.Util;
using Sys.Workflow.Engine.Repository;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Sys.Workflow.Engine.dynamic.MoveExecutionEntityContainer;

namespace Sys.Workflow.Engine.dynamic
{
    /* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


    /**
     * @author Tijs Rademakers
     * @author Dennis Federico
     */
    public abstract class AbstractDynamicStateManager
    {
        private readonly ILogger<AbstractDynamicStateManager> LOGGER = ProcessEngineServiceProvider.LoggerService<AbstractDynamicStateManager>();
        public List<EnableActivityContainer> ResolveEnableActivityContainers(ChangeActivityStateBuilderImpl changeActivityStateBuilder)
        {
            List<EnableActivityContainer> enableActivityContainerList = new List<EnableActivityContainer>();
            if (!changeActivityStateBuilder.GetEnableActivityIdList().IsEmpty())
            {
                foreach (EnableActivityIdContainer enableActivityIdContainer in changeActivityStateBuilder.GetEnableActivityIdList())
                {
                    EnableActivityContainer enableActivityContainer = new EnableActivityContainer(enableActivityIdContainer.GetActivityIds());
                    enableActivityContainerList.Add(enableActivityContainer);
                }
            }

            return enableActivityContainerList;
        }

        public List<MoveExecutionEntityContainer> ResolveMoveExecutionEntityContainers(ChangeActivityStateBuilderImpl changeActivityStateBuilder,
          Dictionary<string, object> variables, ICommandContext commandContext)
        {

            List<MoveExecutionEntityContainer> moveExecutionEntityContainerList = new List<MoveExecutionEntityContainer>();
            if (changeActivityStateBuilder.GetMoveExecutionIdList().Count() > 0)
            {
                foreach (MoveExecutionIdContainer executionContainer in changeActivityStateBuilder.GetMoveExecutionIdList())
                {

                    Dictionary<string, List<IExecutionEntity>> executionsByParent = new Dictionary<string, List<IExecutionEntity>>();
                    Dictionary<string, List<IExecutionEntity>> miExecutionsByParent = new Dictionary<string, List<IExecutionEntity>>();
                    foreach (string executionId in executionContainer.GetExecutionIds())
                    {
                        IExecutionEntity execution = ResolveActiveExecution(executionId, commandContext);
                        List<IExecutionEntity> executionEntities;
                        if (!execution.IsMultiInstanceRoot)
                        {
                            if (!executionsByParent.TryGetValue(execution.ParentId, out executionEntities))
                            {
                                executionEntities = new List<IExecutionEntity>();
                                executionsByParent[execution.ParentId] = executionEntities;
                            }
                        }
                        else
                        {
                            if (!miExecutionsByParent.TryGetValue(execution.ParentId, out executionEntities))
                            {
                                executionEntities = new List<IExecutionEntity>();
                                miExecutionsByParent[execution.ParentId] = executionEntities;
                            }
                        }
                        executionEntities.Add(execution);
                    }

                    miExecutionsByParent.Values.ForEach(executions =>
                    {
                        MoveExecutionEntityContainer moveExecutionEntityContainer = new MoveExecutionEntityContainer(executions, executionContainer.GetMoveToActivityIds());
                        if (executionsByParent.ContainsKey(executions[0].Id))
                        {
                            moveExecutionEntityContainer.SetMultiInstanceExecutionWithChildExecutions(true);
                        }

                        if (executions[0].VariablesLocal != null && !executions[0].VariablesLocal.IsEmpty())
                        {
                            moveExecutionEntityContainer.AddLocalVariableMap(executions[0].ActivityId, executions[0].VariablesLocal as Dictionary<string, object>);
                        }
                        if (executionContainer.GetNewAssigneeId() != null)
                        {
                            moveExecutionEntityContainer.SetNewAssigneeId(executionContainer.GetNewAssigneeId());
                        }
                        if (executionContainer.GetNewOwnerId() != null)
                        {
                            moveExecutionEntityContainer.SetNewOwnerId(executionContainer.GetNewOwnerId());
                        }

                        moveExecutionEntityContainerList.Add(moveExecutionEntityContainer);
                    });

                    executionsByParent.Values.ForEach(executions =>
                    {
                        if (!miExecutionsByParent.IsEmpty() && executions.Count() > 1 && (executions[0].CurrentFlowElement is Task || executions[0].CurrentFlowElement is CallActivity))
                        {
                            foreach (IExecutionEntity execution in executions)
                            {
                                List<IExecutionEntity> miExecutionList = new List<IExecutionEntity>();
                                miExecutionList.Add(execution);
                                MoveExecutionEntityContainer moveExecutionEntityContainer = new MoveExecutionEntityContainer(miExecutionList, executionContainer.GetMoveToActivityIds());

                                if (execution.VariablesLocal != null && !execution.VariablesLocal.IsEmpty())
                                {
                                    moveExecutionEntityContainer.AddLocalVariableMap(execution.ActivityId, execution.VariablesLocal as Dictionary<string, object>);
                                }

                                if (executionContainer.GetNewAssigneeId() != null)
                                {
                                    moveExecutionEntityContainer.SetNewAssigneeId(executionContainer.GetNewAssigneeId());
                                }
                                if (executionContainer.GetNewOwnerId() != null)
                                {
                                    moveExecutionEntityContainer.SetNewOwnerId(executionContainer.GetNewOwnerId());
                                }
                                moveExecutionEntityContainerList.Add(moveExecutionEntityContainer);
                            }

                        }
                        else
                        {
                            MoveExecutionEntityContainer moveExecutionEntityContainer = new MoveExecutionEntityContainer(executions, executionContainer.GetMoveToActivityIds());
                            foreach (IExecutionEntity execution in executions)
                            {
                                if (execution.VariablesLocal != null && !execution.VariablesLocal.IsEmpty())
                                {
                                    moveExecutionEntityContainer.AddLocalVariableMap(execution.ActivityId, execution.VariablesLocal as Dictionary<string, object>);
                                }
                            }
                            if (executionContainer.GetNewAssigneeId() != null)
                            {
                                moveExecutionEntityContainer.SetNewAssigneeId(executionContainer.GetNewAssigneeId());
                            }
                            if (executionContainer.GetNewOwnerId() != null)
                            {
                                moveExecutionEntityContainer.SetNewOwnerId(executionContainer.GetNewOwnerId());
                            }
                            moveExecutionEntityContainerList.Add(moveExecutionEntityContainer);
                        }
                    });
                }
            }

            if (changeActivityStateBuilder.GetMoveActivityIdList().Count() > 0)
            {
                foreach (MoveActivityIdContainer activityContainer in changeActivityStateBuilder.GetMoveActivityIdList())
                {
                    Dictionary<string, List<IExecutionEntity>> activitiesExecutionsByMultiInstanceParentId = new Dictionary<string, List<IExecutionEntity>>();
                    List<IExecutionEntity> activitiesExecutionsNotInMultiInstanceParent = new List<IExecutionEntity>();

                    foreach (string activityId in activityContainer.GetActivityIds())
                    {
                        List<IExecutionEntity> activityExecutions = ResolveActiveExecutions(changeActivityStateBuilder.GetProcessInstanceId(), activityId, commandContext);
                        if (!activityExecutions.IsEmpty())
                        {

                            // check for a multi instance root execution
                            IExecutionEntity miExecution = null;
                            bool isInsideMultiInstance = false;
                            foreach (IExecutionEntity possibleMIExecution in activityExecutions)
                            {
                                if (possibleMIExecution.IsMultiInstanceRoot)
                                {
                                    miExecution = possibleMIExecution;
                                    isInsideMultiInstance = true;
                                    break;
                                }

                                if (IsExecutionInsideMultiInstance(possibleMIExecution))
                                {
                                    isInsideMultiInstance = true;
                                }
                            }

                            // If inside a multiInstance, we create one container for each execution
                            if (isInsideMultiInstance)
                            {

                                // We group by the parentId (executions belonging to the same parent execution instance
                                // i.e. gateways nested in MultiInstance subProcesses, need to be in the same move container)
                                //Stream<IExecutionEntity> executionEntitiesStream = activityExecutions.stream();
                                //if (miExecution != null)
                                //{
                                //    executionEntitiesStream = executionEntitiesStream.filter(ExecutionEntity::isMultiInstanceRoot);
                                //}

                                //executionEntitiesStream.ForEach(childExecution =>
                                //{
                                //    string parentId = childExecution.isMultiInstanceRoot() ? childExecution.Id : childExecutionParentId;
                                //    List<IExecutionEntity> executionEntities = activitiesExecutionsByMultiInstanceParentId.computeIfAbsent(parentId, k-> new ArrayList<>());
                                //    executionEntities.Add(childExecution);
                                //});
                                IEnumerable<IExecutionEntity> executionEntitiesStream = activityExecutions;
                                if (miExecution != null)
                                {
                                    executionEntitiesStream = executionEntitiesStream.Where(e => e.IsMultiInstanceRoot);
                                }

                                foreach (var childExecution in executionEntitiesStream)
                                {
                                    string parentId = childExecution.IsMultiInstanceRoot ? childExecution.Id : childExecution.ParentId;
                                    if (!activitiesExecutionsByMultiInstanceParentId.TryGetValue(parentId, out var executionEntities))
                                    {
                                        executionEntities = new List<IExecutionEntity>();
                                        activitiesExecutionsByMultiInstanceParentId[parentId] = executionEntities;
                                    }
                                    executionEntities.Add(childExecution);
                                }

                            }
                            else
                            {
                                IExecutionEntity execution = activityExecutions.First();
                                activitiesExecutionsNotInMultiInstanceParent.Add(execution);
                            }
                        }
                    }

                    List<IExecutionEntity> combinedExecutions = new List<IExecutionEntity>();
                    if (!activitiesExecutionsByMultiInstanceParentId.IsEmpty())
                    {
                        foreach (string parentId in activitiesExecutionsByMultiInstanceParentId.Keys)
                        {
                            List<IExecutionEntity> miExecutions = activitiesExecutionsByMultiInstanceParentId[parentId];
                            if (!miExecutions.IsEmpty())
                            {
                                if (IsTopLevelMultiInstanceRoot(miExecutions[0]))
                                {
                                    combinedExecutions.AddRange(miExecutions);
                                }
                                else
                                {
                                    moveExecutionEntityContainerList.Add(CreateMoveExecutionEntityContainer(activityContainer, miExecutions, commandContext));
                                }
                            }
                        }
                    }

                    if (!activitiesExecutionsNotInMultiInstanceParent.IsEmpty())
                    {
                        combinedExecutions.AddRange(activitiesExecutionsNotInMultiInstanceParent);
                    }

                    if (!combinedExecutions.IsEmpty())
                    {
                        moveExecutionEntityContainerList.Add(CreateMoveExecutionEntityContainer(activityContainer, combinedExecutions, commandContext));
                    }
                }
            }

            return moveExecutionEntityContainerList;
        }

        protected bool isExecutionInsideMultiInstance(IExecutionEntity execution)
        {
            return GetFlowElementMultiInstanceParentId(execution.CurrentFlowElement) != null;
        }

        protected List<IExecutionEntity> ResolveActiveExecutions(string processInstanceId, string activityId, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);
            IExecutionEntity processExecution = executionEntityManager.FindById<IExecutionEntity>(processInstanceId);

            if (processExecution == null)
            {
                throw new ActivitiException("Execution could not be found with id " + processInstanceId);
            }

            if (!processExecution.ProcessInstanceType)
            {
                throw new ActivitiException("Execution is not a process instance type execution for id " + processInstanceId);
            }

            //if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, processExecution.GetProcessDefinitionId()))
            //{
            //    throw new ActivitiException("Flowable 5 process definitions are not supported");
            //}

            List<IExecutionEntity> childExecutions = executionEntityManager.FindChildExecutionsByProcessInstanceId(processExecution.Id).ToList();

            //List<ExecutionEntity> executions = childExecutions.stream()
            //    .filter(e->e.CurrentActivityId != null)
            //    .filter(e->e.CurrentActivityId.equals(activityId))
            //    .collect(Collectors.toList());
            var executions = childExecutions
                .Where(e => e.CurrentActivityId != null)
                .Where(e => e.CurrentActivityId == activityId)
                .ToList();

            if (executions.IsEmpty())
            {
                throw new ActivitiException("Active execution could not be found with activity id " + activityId);
            }

            return executions;
        }


        protected MoveExecutionEntityContainer CreateMoveExecutionEntityContainer(MoveActivityIdContainer activityContainer, List<IExecutionEntity> executions, ICommandContext commandContext)
        {
            MoveExecutionEntityContainer moveExecutionEntityContainer = new MoveExecutionEntityContainer(executions, activityContainer.GetMoveToActivityIds());
            //activityContainer.GetNewAssigneeId().ifPresent(moveExecutionEntityContainer::setNewAssigneeId);
            //activityContainer.GetNewOwnerId().ifPresent(moveExecutionEntityContainer::setNewOwnerId);
            var assigneeId = activityContainer.GetNewAssigneeId();
            if (assigneeId.HasValue)
            {
                moveExecutionEntityContainer.SetNewAssigneeId(assigneeId.Value);
            }

            var ownerId = activityContainer.GetNewOwnerId();
            if (ownerId.HasValue)
            {
                moveExecutionEntityContainer.SetNewOwnerId(ownerId.Value);
            }
            if (activityContainer.IsMoveToParentProcess())
            {
                IExecutionEntity processInstanceExecution = executions[0].ProcessInstance;
                IExecutionEntity superExecution = processInstanceExecution.SuperExecution;
                if (superExecution == null)
                {
                    throw new ActivitiException("No parent process found for execution with activity id " + executions[0].CurrentActivityId);
                }

                moveExecutionEntityContainer.SetMoveToParentProcess(true);
                moveExecutionEntityContainer.SetSuperExecution(superExecution);

            }
            else if (activityContainer.IsMoveToSubProcessInstance())
            {
                moveExecutionEntityContainer.SetMoveToSubProcessInstance(true);
                moveExecutionEntityContainer.SetCallActivityId(activityContainer.GetCallActivityId());
                moveExecutionEntityContainer.SetCallActivitySubProcessVersion(activityContainer.GetCallActivitySubProcessVersion());
            }
            return moveExecutionEntityContainer;
        }
        protected bool IsTopLevelMultiInstanceRoot(IExecutionEntity execution)
        {
            bool topLevelMultiInstanceRoot = false;
            if (execution.IsMultiInstanceRoot)
            {
                topLevelMultiInstanceRoot = true;
                IExecutionEntity parentExecution = execution.Parent;
                while (parentExecution != null)
                {
                    if (parentExecution.IsMultiInstanceRoot)
                    {
                        topLevelMultiInstanceRoot = false;
                        break;
                    }

                    parentExecution = parentExecution.Parent;
                }
            }

            return topLevelMultiInstanceRoot;
        }
        protected IExecutionEntity ResolveActiveExecution(string executionId, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);
            IExecutionEntity execution = executionEntityManager.FindById<IExecutionEntity>(executionId);

            if (execution == null)
            {
                throw new ActivitiException("Execution could not be found with id " + executionId);
            }

            //if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, execution.GetProcessDefinitionId()))
            //{
            //    throw new FlowableException("Flowable 5 process definitions are not supported");
            //}

            return execution;
        }

        protected abstract Dictionary<string, List<IExecutionEntity>> ResolveActiveEmbeddedSubProcesses(string processInstanceId, ICommandContext commandContext);

        public void DoMoveExecutionState(ProcessInstanceChangeState processInstanceChangeState, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);
            //Dictionary<string, List<IExecutionEntity>> activeEmbeddedSubProcesses = ResolveActiveEmbeddedSubProcesses(processInstanceChangeState.GetProcessInstanceId(), commandContext);
            //processInstanceChangeState.SetProcessInstanceActiveEmbeddedExecutions(activeEmbeddedSubProcesses);

            //Set the processInstance variables first so they are available during the change state
            IExecutionEntity processInstanceExecution = executionEntityManager.FindById<IExecutionEntity>(processInstanceChangeState.GetProcessInstanceId());
            processInstanceExecution.Variables = processInstanceChangeState.GetProcessInstanceVariables();

            foreach (MoveExecutionEntityContainer moveExecutionContainer in processInstanceChangeState.GetMoveExecutionEntityContainers())
            {
                PrepareMoveExecutionEntityContainer(moveExecutionContainer, processInstanceChangeState.GetProcessDefinitionToMigrateTo(), commandContext);
                // Action the moves (changeState)
                if (moveExecutionContainer.IsMoveToParentProcess())
                {
                    string callActivityInstanceId = moveExecutionContainer.GetExecutions()[0].ProcessInstanceId;
                    string deleteReason = "Change activity to parent process activity ids: " + PrintFlowElementIds(moveExecutionContainer.GetMoveToFlowElements());
                    SafeDeleteSubProcessInstance(callActivityInstanceId, moveExecutionContainer.GetExecutions(), deleteReason, commandContext);
                }

                List<IExecutionEntity> executionsToMove;
                if (moveExecutionContainer.IsMoveToParentProcess())
                {
                    executionsToMove = new List<IExecutionEntity>() { moveExecutionContainer.GetSuperExecution() };
                }
                else
                {
                    executionsToMove = moveExecutionContainer.GetExecutions();
                }

                List<FlowElementMoveEntry> moveToFlowElements;
                if (moveExecutionContainer.IsMoveToSubProcessInstance())
                {
                    moveToFlowElements = new List<FlowElementMoveEntry>() { new FlowElementMoveEntry(moveExecutionContainer.GetCallActivity(), moveExecutionContainer.GetCallActivity()) };
                }
                else
                {
                    moveToFlowElements = moveExecutionContainer.GetMoveToFlowElements();
                }

                string flowElementIdsLine = PrintFlowElementIds(moveToFlowElements);
                List<string> executionIdsNotToDelete = new List<string>();
                foreach (IExecutionEntity execution in executionsToMove)
                {
                    executionIdsNotToDelete.Add(execution.Id);

                    // Don't delete called process when directly migration CallActivity
                    List<string> childExecutionsToKeep = new List<string>();
                    if (execution.CurrentFlowElement is CallActivity && moveExecutionContainer.IsDirectExecutionMigration())
                    {
                        //executionEntityManager.collectChildren(execution).stream()
                        //        .filter(IExecutionEntity-> !Objects.equals(IExecutionEntity.ProcessDefinitionId, execution.GetProcessDefinitionId()))
                        //        .Dictionary(IExecutionEntity::getId)
                        //        .forEach(childExecutionsToKeep::add);
                        foreach (var child in executionEntityManager.CollectChildren(execution).Where(e => e.ProcessDefinitionId != execution.ProcessDefinitionId))
                        {
                            childExecutionsToKeep.Add(child.Id);
                        }
                    }

                    if (execution.CurrentFlowElement is Activity && moveExecutionContainer.IsDirectExecutionMigration())
                    {
                        FlowElementMoveEntry moveEntry = moveExecutionContainer.GetCurrentActivityToNewElement(execution.CurrentFlowElement.Id);
                        if (moveEntry != null)
                        {
                            //if (moveEntry.GetOriginalFlowElement().Id.Equals(moveEntry.GetNewFlowElement().Id) &&
                            //        moveEntry.GetOriginalFlowElement().GetClass().GetName().equals(moveEntry.GetNewFlowElement().GetClass().GetName()))
                            if (moveEntry.GetOriginalFlowElement().Id == moveEntry.GetNewFlowElement().Id && moveEntry.GetOriginalFlowElement().GetType() == moveEntry.GetNewFlowElement().GetType())
                            {

                                List<BoundaryEvent> newBoundaryEvents = ((Activity)moveEntry.GetNewFlowElement()).BoundaryEvents.ToList();
                                Dictionary<string, BoundaryEvent> newBoundaryEventDictionary = new Dictionary<string, BoundaryEvent>();
                                if (newBoundaryEvents != null && !newBoundaryEvents.IsEmpty())
                                {
                                    foreach (BoundaryEvent newBoundaryEvent in newBoundaryEvents)
                                    {
                                        newBoundaryEventDictionary.Add(newBoundaryEvent.Id, newBoundaryEvent);
                                    }
                                }

                                List<BoundaryEvent> boundaryEvents = ((Activity)execution.CurrentFlowElement).BoundaryEvents.ToList();
                                if (boundaryEvents != null && !boundaryEvents.IsEmpty())
                                {
                                    foreach (IExecutionEntity executionEntity in executionEntityManager.CollectChildren(execution))
                                    {
                                        if (executionEntity.CurrentFlowElement is BoundaryEvent boundaryEvent)
                                        {
                                            if (newBoundaryEventDictionary.ContainsKey(boundaryEvent.Id) && SameBoundaryEventDefinition(boundaryEvent, newBoundaryEventDictionary[boundaryEvent.Id]))
                                            {

                                                bool hasEventSubscriptions = false;
                                                if (boundaryEvent.EventDefinitions != null && !boundaryEvent.EventDefinitions.IsEmpty())
                                                {
                                                    EventDefinition sourceEventDef = boundaryEvent.EventDefinitions[0];
                                                    if (sourceEventDef is SignalEventDefinition || sourceEventDef is MessageEventDefinition)
                                                    {
                                                        hasEventSubscriptions = true;
                                                    }

                                                }
                                                else if (!boundaryEvent.ExtensionElements.IsEmpty())
                                                {
                                                    List<ExtensionElement> sourceEventTypeExtensionElements = boundaryEvent.ExtensionElements[BpmnXMLConstants.ELEMENT_EVENT_TYPE].ToList();
                                                    if (sourceEventTypeExtensionElements != null && !sourceEventTypeExtensionElements.IsEmpty())
                                                    {
                                                        string sourceEventTypeValue = sourceEventTypeExtensionElements[0].ElementText;
                                                        if (StringUtils.IsNullOrEmpty(sourceEventTypeValue))
                                                        {
                                                            hasEventSubscriptions = true;
                                                        }
                                                    }
                                                }

                                                List<IEventSubscriptionEntity> eventSubscriptions = null;
                                                if (hasEventSubscriptions)
                                                {
                                                    throw new ActivitiException("Cannot move boundary event with event subscriptions");
                                                    //eventSubscriptions = CommandContextUtil.GetEventSubscriptionService(commandContext).findEventSubscriptionsByExecution(executionEntity.Id);
                                                }

                                                if (!hasEventSubscriptions || (eventSubscriptions != null && !eventSubscriptions.IsEmpty()))
                                                {
                                                    executionEntity.ProcessDefinitionId = processInstanceChangeState.GetProcessDefinitionToMigrateTo().Id;
                                                    childExecutionsToKeep.Add(executionEntity.Id);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    executionEntityManager.DeleteChildExecutions(execution, "Change parent activity to " + flowElementIdsLine, true);
                    if (!moveExecutionContainer.IsDirectExecutionMigration())
                    {
                        executionEntityManager.DeleteExecutionAndRelatedData(execution, "Change activity to " + flowElementIdsLine, true);
                    }

                    // Make sure we are not moving the root execution
                    if (execution.ParentId == null)
                    {
                        throw new ActivitiException("Execution has no parent execution " + execution.ParentId);
                    }

                    // Delete the parent executions for each current execution when the move to activity id has the same subProcess scope
                    IExecutionEntity continueParentExecution;
                    if (processInstanceChangeState.GetProcessDefinitionToMigrateTo() != null)
                    {
                        continueParentExecution = DeleteDirectParentExecutions(execution.ParentId, moveToFlowElements,
                                executionIdsNotToDelete, processInstanceChangeState.GetProcessDefinitionToMigrateTo(), moveExecutionContainer, commandContext);
                    }
                    else
                    {
                        continueParentExecution = DeleteParentExecutions(execution.ParentId, moveToFlowElements, executionIdsNotToDelete, commandContext);
                    }
                    moveExecutionContainer.AddContinueParentExecution(execution.Id, continueParentExecution);
                }

                List<IExecutionEntity> newChildExecutions = CreateEmbeddedSubProcessAndExecutions(moveToFlowElements, executionsToMove, moveExecutionContainer, processInstanceChangeState, commandContext);

                if (moveExecutionContainer.IsMoveToSubProcessInstance())
                {
                    CallActivity callActivity = moveExecutionContainer.GetCallActivity();
                    Process subProcess = moveExecutionContainer.GetSubProcessModel().GetProcessById(moveExecutionContainer.GetSubProcessDefKey());
                    IExecutionEntity callActivityInstanceExecution = CreateCallActivityInstance(callActivity, moveExecutionContainer.GetSubProcessDefinition(), newChildExecutions[0], subProcess.InitialFlowElement.Id, commandContext);
                    List<IExecutionEntity> moveExecutions = moveExecutionContainer.GetExecutions();
                    MoveExecutionEntityContainer subProcessMoveIExecutionEntityContainer = new MoveExecutionEntityContainer(moveExecutions, moveExecutionContainer.GetMoveToActivityIds());
                    subProcessMoveIExecutionEntityContainer.SetNewAssigneeId(moveExecutionContainer.GetNewAssigneeId());
                    subProcessMoveIExecutionEntityContainer.SetNewOwnerId(moveExecutionContainer.GetNewOwnerId());
                    moveExecutions.ForEach(ExecutionEntity => subProcessMoveIExecutionEntityContainer.AddContinueParentExecution(ExecutionEntity.Id, callActivityInstanceExecution));
                    newChildExecutions = CreateEmbeddedSubProcessAndExecutions(moveExecutionContainer.GetMoveToFlowElements(), moveExecutions, subProcessMoveIExecutionEntityContainer, new ProcessInstanceChangeState(), commandContext);
                }

                if (!processInstanceChangeState.GetLocalVariables().IsEmpty())
                {
                    Dictionary<string, Dictionary<string, object>> localVariables = processInstanceChangeState.GetLocalVariables();
                    //Iterator<IExecutionEntity> newChildExecutionsIterator = newChildExecutions.iterator();
                    var newChildExecutionsIterator = newChildExecutions.GetEnumerator();
                    while (newChildExecutionsIterator.MoveNext())
                    {
                        //With changeState Api we can set local variables to the parents of moved executions (i.e. subProcesses created during the move)
                        //Thus we traverse up in the hierarchy from the newly created executions
                        IExecutionEntity execution = newChildExecutionsIterator.Current;
                        while (execution != null)
                        {
                            if (execution.ActivityId != null && localVariables.ContainsKey(execution.ActivityId))
                            {
                                if (execution.IsScope || execution.CurrentFlowElement is UserTask)
                                {
                                    execution.VariablesLocal = localVariables[execution.ActivityId];
                                }
                                else
                                {
                                    IExecutionEntity scopedExecutionCandidate = execution;
                                    while (scopedExecutionCandidate.Parent != null)
                                    {
                                        IExecutionEntity parentExecution = scopedExecutionCandidate.Parent;
                                        if (parentExecution.IsScope)
                                        {
                                            parentExecution.VariablesLocal = localVariables[execution.ActivityId];
                                            break;
                                        }

                                        scopedExecutionCandidate = scopedExecutionCandidate.Parent;
                                    }
                                }
                            }
                            execution = execution.Parent;
                        }
                    }
                }

                if (!moveExecutionContainer.IsDirectExecutionMigration())
                {
                    foreach (IExecutionEntity newChildExecution in newChildExecutions)
                    {
                        if (moveExecutionContainer.GetNewAssigneeId() != null && moveExecutionContainer.HasNewExecutionId(newChildExecution.Id))
                        {
                            MigrationContext migrationContext = new MigrationContext();
                            migrationContext.SetAssignee(moveExecutionContainer.GetNewAssigneeId());
                            commandContext.Agenda.PlanContinueProcessWithMigrationContextOperation(newChildExecution, migrationContext);

                        }
                        else if (moveExecutionContainer.GetNewOwnerId() != null && moveExecutionContainer.HasNewExecutionId(newChildExecution.Id))
                        {
                            MigrationContext migrationContext = new MigrationContext();
                            migrationContext.SetOwner(moveExecutionContainer.GetNewOwnerId());
                            //PlanContinueProcessWithMigrationContextOperation
                            commandContext.Agenda.PlanContinueProcessWithMigrationContextOperation(newChildExecution, migrationContext);

                        }
                        else
                        {
                            if (newChildExecution.IsMultiInstanceRoot && moveExecutionContainer.IsMultiInstanceExecutionWithChildExecutions() &&
                                    (newChildExecution.CurrentFlowElement is Task || newChildExecution.CurrentFlowElement is CallActivity))
                            {

                                continue;
                            }

                            if (newChildExecution.CurrentFlowElement is UserTask task && task.Asynchronous)
                            {
                                IJobEntity job = commandContext.JobManager.CreateAsyncJob(newChildExecution, task.Exclusive);
                                commandContext.JobManager.ScheduleAsyncJob(job);

                                //JobService jobService = CommandContextUtil.GetJobService(commandContext);

                                //JobEntity job = JobUtil.createJob(newChildExecution, newChildExecution.CurrentFlowElement, AsyncContinuationJobHandler.TYPE, CommandContextUtil.GetProcessEngineConfiguration(commandContext));

                                //jobService.createAsyncJob(job, task.isExclusive());
                                //jobService.scheduleAsyncJob(job);

                            }
                            else
                            {
                                commandContext.Agenda.PlanContinueProcessOperation(newChildExecution);
                            }
                        }
                    }
                }
            }

            ProcessPendingEventSubProcessesStartEvents(processInstanceChangeState, commandContext);

            foreach (EnableActivityContainer enableActivityContainer in processInstanceChangeState.GetEnableActivityContainers())
            {
                if (enableActivityContainer.GetActivityIds() != null && !enableActivityContainer.GetActivityIds().IsEmpty())
                {
                    BpmnModel bpmnModel = null;
                    IExecutionEntity parentExecution = executionEntityManager.FindById<IExecutionEntity>(processInstanceChangeState.GetProcessInstanceId());
                    if (processInstanceChangeState.GetProcessDefinitionToMigrateTo() != null)
                    {
                        bpmnModel = ProcessDefinitionUtil.GetBpmnModel(processInstanceChangeState.GetProcessDefinitionToMigrateTo().Id);

                    }
                    else
                    {
                        bpmnModel = ProcessDefinitionUtil.GetBpmnModel(parentExecution.ProcessDefinitionId);
                    }

                    ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
                    IProcessInstanceHelper processInstanceHelper = processEngineConfiguration.ProcessInstanceHelper;

                    foreach (string enableActivityId in enableActivityContainer.GetActivityIds())
                    {
                        FlowElement enableFlowElement = bpmnModel.GetFlowElement(enableActivityId);
                        if (enableFlowElement == null)
                        {
                            throw new ActivitiException("could not find element for activity id " + enableActivityId);
                        }

                        processInstanceHelper.ProcessEventSubProcessStartEvent(enableFlowElement, parentExecution, processEngineConfiguration, commandContext);
                    }
                }
            }
        }

        protected bool IsExpression(string variableName)
        {
            return variableName.StartsWith("${") || variableName.StartsWith("#{");
        }

        protected void PrepareMoveExecutionEntityContainer(MoveExecutionEntityContainer moveExecutionContainer, IProcessDefinition migrateToProcessDefinition, ICommandContext commandContext)
        {
            ExpressionManager expressionManager = CommandContextUtil.GetProcessEngineConfiguration(commandContext).ExpressionManager;

            BpmnModel bpmnModelToMigrateTo = null;
            if (migrateToProcessDefinition != null)
            {
                bpmnModelToMigrateTo = ProcessDefinitionUtil.GetBpmnModel(migrateToProcessDefinition.Id);
            }
            bool canContainerDirectMigrate = (moveExecutionContainer.GetMoveToActivityIds().Count() == 1) && (moveExecutionContainer.GetExecutions().Count() == 1);
            foreach (string activityId in moveExecutionContainer.GetMoveToActivityIds())
            {
                FlowElement currentFlowElement;
                FlowElement newFlowElement;
                string currentActivityId;
                if (moveExecutionContainer.IsMoveToParentProcess())
                {
                    string parentProcessDefinitionId = moveExecutionContainer.GetSuperExecution().ProcessDefinitionId;
                    BpmnModel modelOfCallActivity = ProcessDefinitionUtil.GetBpmnModel(moveExecutionContainer.GetExecutions()[0].ProcessDefinitionId);
                    currentActivityId = moveExecutionContainer.GetExecutions()[0].CurrentActivityId;
                    currentFlowElement = ResolveFlowElementFromBpmnModel(modelOfCallActivity, currentActivityId);
                    if (bpmnModelToMigrateTo != null)
                    {
                        newFlowElement = ResolveFlowElementFromBpmnModel(bpmnModelToMigrateTo, activityId);
                    }
                    else
                    {
                        BpmnModel bpmnModel = ProcessDefinitionUtil.GetBpmnModel(parentProcessDefinitionId);
                        newFlowElement = ResolveFlowElementFromBpmnModel(bpmnModel, activityId);
                    }
                    canContainerDirectMigrate = false;

                }
                else if (moveExecutionContainer.IsMoveToSubProcessInstance())
                {
                    //The subProcess model is defined in the callActivity of the current processDefinition or the migrateProcessDefinition if defined
                    IExecutionEntity firstExecution = moveExecutionContainer.GetExecutions()[0];
                    BpmnModel bpmnModel = ProcessDefinitionUtil.GetBpmnModel(firstExecution.ProcessDefinitionId);
                    currentActivityId = firstExecution.CurrentActivityId;
                    currentFlowElement = ResolveFlowElementFromBpmnModel(bpmnModel, currentActivityId);

                    string processDefinitionIdOfCallActivity = null;
                    if (migrateToProcessDefinition != null)
                    {
                        processDefinitionIdOfCallActivity = migrateToProcessDefinition.Id;
                    }
                    else
                    {
                        processDefinitionIdOfCallActivity = firstExecution.ProcessDefinitionId;
                    }

                    CallActivity callActivity = null;
                    if (bpmnModelToMigrateTo != null)
                    {
                        callActivity = (CallActivity)ResolveFlowElementFromBpmnModel(bpmnModelToMigrateTo, moveExecutionContainer.GetCallActivityId());
                    }
                    else
                    {
                        callActivity = (CallActivity)ResolveFlowElementFromBpmnModel(bpmnModel, moveExecutionContainer.GetCallActivityId());
                    }

                    moveExecutionContainer.SetCallActivity(callActivity);
                    IProcessDefinition callActivityProcessDefinition = ProcessDefinitionUtil.GetProcessDefinition(processDefinitionIdOfCallActivity);
                    string tenantId = callActivityProcessDefinition.TenantId;
                    long calledProcessVersion = moveExecutionContainer.GetCallActivitySubProcessVersion();
                    string calledProcessDefKey = callActivity.CalledElement;
                    if (IsExpression(calledProcessDefKey))
                    {
                        try
                        {
                            calledProcessDefKey = expressionManager.CreateExpression(calledProcessDefKey).GetValue(firstExecution.ProcessInstance).ToString();
                        }
                        catch (ActivitiException e)
                        {
                            throw new ActivitiException("Cannot resolve calledElement expression '" + calledProcessDefKey + "' of callActivity '" + callActivity.Id + "'", e);
                        }
                    }
                    moveExecutionContainer.SetSubProcessDefKey(calledProcessDefKey);
                    IProcessDefinition subProcessDefinition = ResolveProcessDefinition(calledProcessDefKey, calledProcessVersion, tenantId, commandContext);
                    BpmnModel subProcessModel = ProcessDefinitionUtil.GetBpmnModel(subProcessDefinition.Id);
                    moveExecutionContainer.SetSubProcessDefinition(subProcessDefinition);
                    moveExecutionContainer.SetSubProcessModel(subProcessModel);

                    newFlowElement = ResolveFlowElementFromBpmnModel(subProcessModel, activityId);
                    canContainerDirectMigrate = false;

                }
                else
                {
                    // Get first execution to get process definition id
                    IExecutionEntity firstExecution = moveExecutionContainer.GetExecutions()[0];
                    BpmnModel bpmnModel = ProcessDefinitionUtil.GetBpmnModel(firstExecution.ProcessDefinitionId);
                    currentActivityId = firstExecution.CurrentActivityId;
                    currentFlowElement = ResolveFlowElementFromBpmnModel(bpmnModel, currentActivityId);
                    if (bpmnModelToMigrateTo != null)
                    {
                        newFlowElement = ResolveFlowElementFromBpmnModel(bpmnModelToMigrateTo, activityId);
                    }
                    else
                    {
                        newFlowElement = ResolveFlowElementFromBpmnModel(bpmnModel, activityId);
                    }
                }

                moveExecutionContainer.AddMoveToFlowElement(activityId, currentFlowElement, newFlowElement);
                moveExecutionContainer.AddCurrentActivityToNewElement(currentActivityId, currentFlowElement, newFlowElement);
                canContainerDirectMigrate = canContainerDirectMigrate && IsDirectFlowElementExecutionMigration(currentFlowElement, newFlowElement);
            }

            moveExecutionContainer.SetDirectExecutionMigration(canContainerDirectMigrate && migrateToProcessDefinition != null);
        }



        protected IProcessDefinition ResolveProcessDefinition(string processDefinitionKey, long processDefinitionVersion, string tenantId, ICommandContext commandContext)
        {
            IProcessDefinitionEntityManager processDefinitionEntityManager = CommandContextUtil.GetProcessDefinitionEntityManager(commandContext);
            IProcessDefinition processDefinition;
            if (processDefinitionVersion != null)
            {
                processDefinition = processDefinitionEntityManager.FindProcessDefinitionByKeyAndVersionAndTenantId(processDefinitionKey, (int)processDefinitionVersion, tenantId);
            }
            else
            {
                if (tenantId == null || ProcessEngineConfiguration.NO_TENANT_ID.Equals(tenantId))
                {
                    processDefinition = processDefinitionEntityManager.FindLatestProcessDefinitionByKey(processDefinitionKey);
                }
                else
                {
                    processDefinition = processDefinitionEntityManager.FindLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, tenantId);
                }
            }

            if (processDefinition == null)
            {
                DeploymentManager deploymentManager = CommandContextUtil.GetProcessEngineConfiguration(commandContext).DeploymentManager;
                if (tenantId == null || ProcessEngineConfiguration.NO_TENANT_ID.Equals(tenantId))
                {
                    processDefinition = deploymentManager.FindDeployedLatestProcessDefinitionByKey(processDefinitionKey);
                }
                else
                {
                    processDefinition = deploymentManager.FindDeployedLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, tenantId);
                }
            }
            return processDefinition;
        }

        protected abstract bool IsDirectFlowElementExecutionMigration(FlowElement currentFlowElement, FlowElement newFlowElement);
        protected FlowElement ResolveFlowElementFromBpmnModel(BpmnModel bpmnModel, string activityId)
        {
            FlowElement flowElement = bpmnModel.GetFlowElement(activityId);
            if (flowElement == null)
            {
                throw new ActivitiException("Cannot find activity '" + activityId + "' in process definition with id '" + bpmnModel.MainProcess.Id + "'");
            }
            return flowElement;
        }

        protected bool SameBoundaryEventDefinition(BoundaryEvent sourceEvent, BoundaryEvent targetEvent)
        {
            if (sourceEvent.EventDefinitions != null && !sourceEvent.EventDefinitions.IsEmpty() &&
                    (targetEvent.EventDefinitions == null || targetEvent.EventDefinitions.IsEmpty()))
            {

                return false;

            }
            else if ((sourceEvent.EventDefinitions == null || sourceEvent.EventDefinitions.IsEmpty()) &&
                    targetEvent.EventDefinitions != null && !targetEvent.EventDefinitions.IsEmpty())
            {

                return false;
            }

            if (sourceEvent.EventDefinitions != null && !sourceEvent.EventDefinitions.IsEmpty())
            {
                EventDefinition sourceEventDef = sourceEvent.EventDefinitions[0];
                EventDefinition targetEventDef = targetEvent.EventDefinitions[0];

                if (sourceEventDef.GetType() != targetEventDef.GetType())
                {
                    return false;
                }

                if (sourceEventDef is SignalEventDefinition signalSourceDef)
                {
                    SignalEventDefinition signalTargetDef = (SignalEventDefinition)targetEventDef;

                    if (StringUtils.IsNullOrEmpty(signalSourceDef.SignalRef) && signalSourceDef.SignalRef.Equals(signalTargetDef.SignalRef))
                    {
                        return true;
                    }

                    if (StringUtils.IsNullOrEmpty(signalSourceDef.SignalExpression) && signalSourceDef.SignalExpression.Equals(signalTargetDef.SignalRef))
                    {
                        return true;
                    }

                }
                else if (sourceEventDef is MessageEventDefinition messageSourceDef)
                {
                    MessageEventDefinition messageTargetDef = (MessageEventDefinition)targetEventDef;

                    if (StringUtils.IsNullOrEmpty(messageSourceDef.MessageRef) && messageSourceDef.MessageRef.Equals(messageTargetDef.MessageRef))
                    {
                        return true;
                    }

                    if (StringUtils.IsNullOrEmpty(messageSourceDef.MessageExpression) && messageSourceDef.MessageExpression.Equals(messageTargetDef.MessageExpression))
                    {
                        return true;
                    }

                }
                else if (sourceEventDef is TimerEventDefinition timerSourceDef)
                {
                    TimerEventDefinition timerTargetDef = (TimerEventDefinition)targetEventDef;

                    if (StringUtils.IsNullOrEmpty(timerSourceDef.TimeCycle) && timerSourceDef.TimeCycle.Equals(timerTargetDef.TimeCycle))
                    {
                        return true;
                    }

                    if (StringUtils.IsNullOrEmpty(timerSourceDef.TimeDate) && timerSourceDef.TimeDate.Equals(timerTargetDef.TimeDate))
                    {
                        return true;
                    }

                    if (StringUtils.IsNullOrEmpty(timerSourceDef.TimeDuration) && timerSourceDef.TimeDuration.Equals(timerTargetDef.TimeDuration))
                    {
                        return true;
                    }
                }

            }
            else if (!sourceEvent.ExtensionElements.IsEmpty() && !targetEvent.ExtensionElements.IsEmpty())
            {
                List<ExtensionElement> sourceEventTypeExtensionElements = sourceEvent.ExtensionElements[BpmnXMLConstants.ELEMENT_EVENT_TYPE].ToList();
                List<ExtensionElement> targetEventTypeExtensionElements = targetEvent.ExtensionElements[BpmnXMLConstants.ELEMENT_EVENT_TYPE].ToList();
                string sourceEventTypeValue = null;
                if (sourceEventTypeExtensionElements != null && !sourceEventTypeExtensionElements.IsEmpty())
                {
                    sourceEventTypeValue = sourceEventTypeExtensionElements[0].ElementText;
                }

                string targetEventTypeValue = null;
                if (targetEventTypeExtensionElements != null && !targetEventTypeExtensionElements.IsEmpty())
                {
                    targetEventTypeValue = targetEventTypeExtensionElements[0].ElementText;
                }

                if (StringUtils.IsNullOrEmpty(sourceEventTypeValue) && StringUtils.IsNullOrEmpty(targetEventTypeValue) &&
                        sourceEventTypeValue.Equals(targetEventTypeValue))
                {

                    return true;
                }
            }

            return false;
        }

        protected void HandleUserTaskNewAssignee(IExecutionEntity taskExecution, string newAssigneeId, ICommandContext commandContext)
        {
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            ITaskService taskService = processEngineConfiguration.TaskService;
            TaskEntityImpl task = (TaskEntityImpl)taskService.CreateTaskQuery()//(processEngineConfiguration.CommandExecutor, processEngineConfiguration)
                    .SetExecutionId(taskExecution.Id).SingleResult();
            if (task != null)
            {
                TaskHelper.ChangeTaskAssignee(task, newAssigneeId);
            }
        }

        protected IExecutionEntity MigrateExecutionEntity(IExecutionEntity parentExecutionEntity, IExecutionEntity childExecution, FlowElement originalElement, FlowElement newFlowElement, ICommandContext commandContext)
        {
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            ITaskService taskService = processEngineConfiguration.TaskService;
            ExpressionManager expressionManager = processEngineConfiguration.ExpressionManager;

            // manage the bidirectional parent-child relation
            childExecution.ProcessInstanceId = parentExecutionEntity.ProcessInstanceId;
            childExecution.ProcessInstance = parentExecutionEntity.ProcessInstance;
            childExecution.ProcessDefinitionId = parentExecutionEntity.ProcessDefinitionId;
            IExecutionEntity oldParent = childExecution.Parent;
            if (oldParent != null && !oldParent.Id.Equals(parentExecutionEntity.Id))
            {
                oldParent.Executions.Remove(childExecution);
            }
            childExecution.Parent = parentExecutionEntity;
            parentExecutionEntity.AddChildExecution(childExecution);

            //Additional changes if the new activity Id doesn't match
            String oldActivityId = childExecution.CurrentActivityId;
            if (!childExecution.CurrentActivityId.Equals(newFlowElement.Id))
            {
                ((ExecutionEntityImpl)childExecution).ActivityId = (newFlowElement.Id);
            }

            // If we are moving a UserTask we need to update its processDefinition references
            if (newFlowElement is UserTask)
            {
                TaskEntityImpl task = (TaskEntityImpl)taskService.CreateTaskQuery()
                        .SetExecutionId(childExecution.Id).SingleResult();
                task.ProcessDefinitionId = childExecution.ProcessDefinitionId;
                task.TaskDefinitionKey = newFlowElement.Id;

                // Set name
                String name = null;
                if (newFlowElement.Name != null)
                {
                    Object nameValue = expressionManager.CreateExpression(newFlowElement.Name).GetValue(childExecution);
                    if (nameValue != null)
                    {
                        name = nameValue.ToString();
                    }
                }
                task.Name = name;

                // Set description
                String description = null;
                if (newFlowElement.Documentation != null)
                {
                    Object descriptionValue = expressionManager.CreateExpression(newFlowElement.Documentation).GetValue(childExecution);
                    if (descriptionValue != null)
                    {
                        description = descriptionValue.ToString();
                    }
                }
                task.Description = description;

                // Set form key
                String newFormKey = null;
                String userTaskFormKey = ((UserTask)newFlowElement).FormKey;
                if (userTaskFormKey != null)
                {
                    Object formKeyValue = expressionManager.CreateExpression(userTaskFormKey).GetValue(childExecution);
                    if (formKeyValue != null)
                    {
                        newFormKey = formKeyValue.ToString();
                    }
                }
                task.FormKey = newFormKey;

                // Set newCategory
                String newCategory = null;
                String userTaskCategory = ((UserTask)newFlowElement).Category;
                if (userTaskCategory != null)
                {
                    Object categoryValue = expressionManager.CreateExpression(userTaskCategory).GetValue(childExecution);
                    if (categoryValue != null)
                    {
                        newCategory = categoryValue.ToString();
                    }
                }
                task.Category = newCategory;

                task.ProcessInstanceId = childExecution.ProcessInstanceId;

                //Sync history
                //processEngineConfiguration.HistoryManager.SyncUserTaskExecution(childExecution, newFlowElement, oldActivityId, task);
            }

            if (newFlowElement is ServiceTask && (((ServiceTask)newFlowElement).Asynchronous))
            {
                HandleServiceTaskJobUpdate(childExecution, commandContext);
            }
            //else if (newFlowElement is IExternalWorkerServiceTask) {
            //    HandleExternalWorkerServiceTaskJobUpdate(childExecution, commandContext);
            //}

            // Boundary Events - only applies to Activities and up to this point we have a UserTask, ReceiveTask or ExternalWorkerServiceTask execution, they are all Activities
            List<BoundaryEvent> finalBoundaryEvents = null;
            if (originalElement == null || !originalElement.Id.Equals(newFlowElement.Id) ||
                    !originalElement.GetType().Name.Equals(newFlowElement.GetType().Name))
            {

                finalBoundaryEvents = ((Activity)newFlowElement).BoundaryEvents.ToList();

            }
            else
            {
                List<BoundaryEvent> boundaryEvents = ((Activity)newFlowElement).BoundaryEvents.ToList();
                if (boundaryEvents != null && !boundaryEvents.IsEmpty())
                {

                    var originalBoundaryEventMap = new Dictionary<string, (BoundaryEvent boundaryEvent, IExecutionEntity executionEntity)>();
                    foreach (IExecutionEntity executionEntity in processEngineConfiguration.ExecutionEntityManager.CollectChildren(childExecution))
                    {
                        if (executionEntity.CurrentFlowElement != null && executionEntity.CurrentFlowElement is BoundaryEvent)
                        {
                            originalBoundaryEventMap.Add(executionEntity.CurrentFlowElement.Id, ((BoundaryEvent)executionEntity.CurrentFlowElement, executionEntity));
                        }
                    }

                    finalBoundaryEvents = new List<BoundaryEvent>();
                    foreach (BoundaryEvent boundaryEvent in boundaryEvents)
                    {
                        if (!originalBoundaryEventMap.ContainsKey(boundaryEvent.Id) || !SameBoundaryEventDefinition(boundaryEvent, originalBoundaryEventMap[boundaryEvent.Id].Item1))
                        {
                            finalBoundaryEvents.Add(boundaryEvent);
                            continue;
                        }

                        bool hasEventSubscriptions = false;
                        if (boundaryEvent.EventDefinitions != null && !boundaryEvent.EventDefinitions.IsEmpty())
                        {
                            EventDefinition sourceEventDef = boundaryEvent.EventDefinitions[0];
                            if (sourceEventDef is SignalEventDefinition || sourceEventDef is MessageEventDefinition)
                            {
                                hasEventSubscriptions = true;
                            }

                        }
                        else if (!boundaryEvent.ExtensionElements.IsEmpty())
                        {
                            List<ExtensionElement> sourceEventTypeExtensionElements = boundaryEvent.ExtensionElements[BpmnXMLConstants.ELEMENT_EVENT_TYPE].ToList();
                            if (sourceEventTypeExtensionElements != null && !sourceEventTypeExtensionElements.IsEmpty())
                            {
                                String sourceEventTypeValue = sourceEventTypeExtensionElements[0].ElementText;
                                if (StringUtils.IsNullOrEmpty(sourceEventTypeValue))
                                {
                                    hasEventSubscriptions = true;
                                }
                            }
                        }

                        //if (hasEventSubscriptions)
                        //{
                        //    List<IEventSubscriptionEntity> eventSubscriptions = CommandContextUtil.GetEventSubscriptionService(commandContext)
                        //            .findEventSubscriptionsByExecution(originalBoundaryEventMap[boundaryEvent.Id].Item2.Id);
                        //    if (eventSubscriptions == null || eventSubscriptions.IsEmpty())
                        //    {
                        //        finalBoundaryEvents.Add(boundaryEvent);
                        //    }
                        //}
                    }
                }
            }

            if (finalBoundaryEvents != null && !finalBoundaryEvents.IsEmpty())
            {
                List<IExecutionEntity> boundaryEventsExecutions = CreateBoundaryEvents(finalBoundaryEvents, childExecution, commandContext);
                ExecuteBoundaryEvents(finalBoundaryEvents, boundaryEventsExecutions);
            }

            if (LOGGER.IsEnabled(LogLevel.Debug))
            {
                LOGGER.LogDebug("Child execution {} updated with parent {}", childExecution, parentExecutionEntity.Id);
            }
            return childExecution;
        }

        protected void HandleServiceTaskJobUpdate(IExecutionEntity childExecution, ICommandContext commandContext)
        {
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            IManagementService managementService = processEngineConfiguration.ManagementService;

            // An existing executable job is not updated, because it's not needed and could lead to optimistic lock exceptions

            // Update an existing timer job
            TimerJobEntityImpl timerJob = (TimerJobEntityImpl)managementService.CreateTimerJobQuery()
                    .SetExecutionId(childExecution.Id).SingleResult();
            if (timerJob != null)
            {
                timerJob.ProcessDefinitionId = childExecution.ProcessDefinitionId;
                return;
            }

            // Update an existing dead letter job
            DeadLetterJobEntityImpl deadLetterJob = (DeadLetterJobEntityImpl)managementService.CreateDeadLetterJobQuery()
                    .SetExecutionId(childExecution.Id).SingleResult();
            if (deadLetterJob != null)
            {
                deadLetterJob.ProcessDefinitionId = childExecution.ProcessDefinitionId;
                return;
            }

            // Update an existing suspended job
            SuspendedJobEntityImpl suspendedJob = (SuspendedJobEntityImpl)managementService.CreateSuspendedJobQuery()
                    .SetExecutionId(childExecution.Id).SingleResult();
            if (suspendedJob != null)
            {
                suspendedJob.ProcessDefinitionId = childExecution.ProcessDefinitionId;
                return;
            }

            // Update an existing external worker job
            //IExternalWorkerJobEntityImpl externalWorkerJob = (ExternalWorkerJobEntityImpl)managementService.createExternalWorkerJobQuery()
            //        .executionId(childExecution.getId()).singleResult();
            //if (externalWorkerJob != null)
            //{
            //    externalWorkerJob.setProcessDefinitionId(childExecution.getProcessDefinitionId());
            //    return;
            //}
        }


        protected List<IExecutionEntity> CreateBoundaryEvents(List<BoundaryEvent> boundaryEvents, IExecutionEntity execution, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);

            List<IExecutionEntity> boundaryEventExecutions = new List<IExecutionEntity>(boundaryEvents.Count());

            // The parent execution becomes a scope, and a child execution is created for each of the boundary events
            foreach (BoundaryEvent boundaryEvent in boundaryEvents)
            {

                if (CollectionUtil.IsEmpty(boundaryEvent.EventDefinitions))
                {

                    bool hasEventRegistryBoundaryEvent = false;
                    if (!boundaryEvent.ExtensionElements.IsEmpty())
                    {
                        List<ExtensionElement> eventTypeExtensionElements = boundaryEvent.ExtensionElements[BpmnXMLConstants.ELEMENT_EVENT_TYPE].ToList();
                        if (eventTypeExtensionElements != null && !eventTypeExtensionElements.IsEmpty())
                        {
                            String eventTypeValue = eventTypeExtensionElements[0].ElementText;
                            if (StringUtils.IsNullOrEmpty(eventTypeValue))
                            {
                                hasEventRegistryBoundaryEvent = true;
                            }
                        }
                    }

                    if (!hasEventRegistryBoundaryEvent)
                    {
                        continue;
                    }

                }
                else if (boundaryEvent.EventDefinitions[0] is CompensateEventDefinition)
                {
                    continue;
                }

                // A Child execution of the current execution is created to represent the boundary event being active
                IExecutionEntity childExecutionEntity = executionEntityManager.CreateChildExecution(execution);
                childExecutionEntity.ParentId = execution.Id;
                childExecutionEntity.CurrentFlowElement = boundaryEvent;
                childExecutionEntity.IsScope = false;
                boundaryEventExecutions.Add(childExecutionEntity);
            }

            return boundaryEventExecutions;
        }


        protected List<IExecutionEntity> CreateEmbeddedSubProcessAndExecutions(List<FlowElementMoveEntry> moveToFlowElements, List<IExecutionEntity> movingExecutions,
           MoveExecutionEntityContainer moveExecutionEntityContainer, ProcessInstanceChangeState processInstanceChangeState, ICommandContext commandContext)
        {

            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            IExecutionEntityManager executionEntityManager = processEngineConfiguration.ExecutionEntityManager;

            // Resolve the sub process elements that need to be created for each move to flow element
            ConcurrentDictionary<string, SubProcess> subProcessesToCreate = new ConcurrentDictionary<string, SubProcess>();
            foreach (FlowElementMoveEntry flowElementMoveEntry in moveToFlowElements)
            {
                FlowElement newFlowElement = flowElementMoveEntry.GetNewFlowElement();
                SubProcess subProcess = newFlowElement.SubProcess;
                //If the new flowElement is the StartEvent of and EventSubProcess, we skip the subProcess creation, the startEvent is contained in a level above
                if (IsEventSubProcessStart(newFlowElement))
                {
                    subProcess = subProcess.SubProcess;
                }
                while (subProcess != null)
                {
                    if (!processInstanceChangeState.GetProcessInstanceActiveEmbeddedExecutions().ContainsKey(subProcess.Id) && !IsSubProcessAncestorOfAnyExecution(subProcess.Id, movingExecutions))
                    {
                        subProcessesToCreate.TryAdd(subProcess.Id, subProcess);
                    }
                    subProcess = subProcess.SubProcess;
                }
            }

            // The default parent execution is retrieved from the match with the first source execution
            IExecutionEntity defaultContinueParentExecution = moveExecutionEntityContainer.GetContinueParentExecution(movingExecutions[0].Id);
            //Set<String> movingExecutionIds = movingExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toSet());
            var movingExecutionIds = new HashSet<string>(movingExecutions.Select(e => e.Id));
            // Build the subProcess hierarchy
            foreach (SubProcess subProcess in subProcessesToCreate.Values)
            {
                if (subProcess is EventSubProcess)
                {
                    IExecutionEntity embeddedSubProcess = CreateEmbeddedSubProcessHierarchy(subProcess, defaultContinueParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
                    moveExecutionEntityContainer.AddCreatedEventSubProcess(subProcess.Id, embeddedSubProcess);
                }
                else if (!processInstanceChangeState.GetCreatedEmbeddedSubProcesses().ContainsKey(subProcess.Id))
                {
                    IExecutionEntity embeddedSubProcess = CreateEmbeddedSubProcessHierarchy(subProcess, defaultContinueParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
                    processInstanceChangeState.AddCreatedEmbeddedSubProcess(subProcess.Id, embeddedSubProcess);
                }
            }

            // Adds the execution (leaf) to the subProcess
            List<IExecutionEntity> newChildExecutions = new List<IExecutionEntity>();
            foreach (FlowElementMoveEntry flowElementMoveEntry in moveToFlowElements)
            {
                FlowElement newFlowElement = flowElementMoveEntry.GetNewFlowElement();
                IExecutionEntity parentExecution;
                if (newFlowElement.SubProcess != null && moveExecutionEntityContainer.GetCreatedEventSubProcess(newFlowElement.SubProcess.Id) != null)
                {
                    parentExecution = moveExecutionEntityContainer.GetCreatedEventSubProcess(newFlowElement.SubProcess.Id);
                }
                else if (newFlowElement.SubProcess != null && processInstanceChangeState.GetCreatedEmbeddedSubProcesses().ContainsKey(newFlowElement.SubProcess.Id))
                {
                    parentExecution = processInstanceChangeState.GetCreatedEmbeddedSubProcesses()[newFlowElement.SubProcess.Id];

                }
                else if ((newFlowElement is TaskActivity || newFlowElement is CallActivity) && IsFlowElementMultiInstance(newFlowElement) && !movingExecutions[0].IsMultiInstanceRoot &&
                                    processInstanceChangeState.GetCreatedMultiInstanceRootExecution().ContainsKey(newFlowElement.Id))
                {

                    parentExecution = processInstanceChangeState.GetCreatedMultiInstanceRootExecution()[newFlowElement.Id];

                }
                else
                {
                    parentExecution = defaultContinueParentExecution;
                }

                if (IsEventSubProcessStart(newFlowElement))
                {
                    // EventSubProcessStarts are created later if the eventSubProcess was not created already during another move
                    processInstanceChangeState.AddPendingEventSubProcessStartEvent((StartEvent)newFlowElement, parentExecution);

                }
                else
                {
                    IExecutionEntity newChildExecution;
                    if (moveExecutionEntityContainer.IsDirectExecutionMigration() && IsDirectFlowElementExecutionMigration(flowElementMoveEntry.GetOriginalFlowElement(), flowElementMoveEntry.GetNewFlowElement()))
                    {
                        newChildExecution = MigrateExecutionEntity(parentExecution, movingExecutions[0], flowElementMoveEntry.GetOriginalFlowElement(), newFlowElement, commandContext);
                    }
                    else
                    {
                        newChildExecution = executionEntityManager.CreateChildExecution(parentExecution);
                        newChildExecution.CurrentFlowElement = newFlowElement;
                        moveExecutionEntityContainer.AddNewExecutionId(newChildExecution.Id);
                    }

                    if (newChildExecution != null)
                    {

                        if (moveExecutionEntityContainer.GetFlowElementLocalVariableMap().ContainsKey(newFlowElement.Id))
                        {
                            newChildExecution.VariablesLocal = moveExecutionEntityContainer.GetFlowElementLocalVariableMap()[newFlowElement.Id];
                        }

                        if (movingExecutions[0].IsMultiInstanceRoot && IsFlowElementMultiInstance(newFlowElement) && HasSameMultiInstanceConfig(movingExecutions[0].CurrentFlowElement, newFlowElement))
                        {
                            newChildExecution.IsMultiInstanceRoot = true;
                            newChildExecution.IsActive = false;
                            processInstanceChangeState.AddCreatedMultiInstanceRootExecution(newFlowElement.Id, newChildExecution);
                        }

                        if (newFlowElement is UserTask
                            && !moveExecutionEntityContainer.HasNewExecutionId(newChildExecution.Id))
                        {

                            if (moveExecutionEntityContainer.GetNewAssigneeId() != null)
                            {
                                HandleUserTaskNewAssignee(newChildExecution, moveExecutionEntityContainer.GetNewAssigneeId(), commandContext);
                            }

                            if (moveExecutionEntityContainer.GetNewOwnerId() != null)
                            {
                                HandleUserTaskNewOwner(newChildExecution, moveExecutionEntityContainer.GetNewOwnerId(), commandContext);
                            }

                        }

                        if (newFlowElement is CallActivity callActivity && !moveExecutionEntityContainer.IsDirectExecutionMigration())
                        {

                            if (!newChildExecution.IsMultiInstanceRoot)
                            {
                                processEngineConfiguration.HistoryManager.RecordActivityStart(newChildExecution);

                                //IActivitiEventDispatcher eventDispatcher = processEngineConfiguration.ActivitiEventDispatcher;
                                //if (eventDispatcher != null && eventDispatcher.Enabled)
                                //{
                                //    eventDispatcher.DispatchEvent(
                                //        ActivitiEventBuilder.CreateActivityEvent(ActivitiEventType.ACTIVITY_STARTED, newFlowElement.Id, newFlowElement.Name, newChildExecution.Id,
                                //            newChildExecution.ProcessInstanceId, newChildExecution.ProcessDefinitionId, newFlowElement));//,processEngineConfiguration.GetEngineCfgKey());
                                //}
                            }

                            // start boundary events of new call activity
                            List<BoundaryEvent> boundaryEvents = callActivity.BoundaryEvents.ToList();
                            if (CollectionUtil.IsNotEmpty(boundaryEvents))
                            {
                                ExecuteBoundaryEvents(boundaryEvents, newChildExecution);
                            }
                        }

                        newChildExecutions.Add(newChildExecution);
                    }

                    // Parallel gateway joins needs each incoming execution to enter the gateway naturally as it checks the number of executions to be able to progress/continue
                    // If we have multiple executions going into a gateway, usually into a gateway join using xxxToSingleActivityId
                    if (newFlowElement is Gateway)
                    {
                        //Skip one that was already added
                        //movingExecutions.stream().skip(1).forEach(e-> {
                        //    ExecutionEntity childExecution = executionEntityManager.createChildExecution(defaultContinueParentExecution);
                        //    childExecution.setCurrentFlowElement(newFlowElement);
                        //    newChildExecutions.add(childExecution);
                        //});
                        foreach (var e in movingExecutions.Skip(1))
                        {
                            var childExecution = executionEntityManager.CreateChildExecution(defaultContinueParentExecution);
                            childExecution.CurrentFlowElement = newFlowElement;
                            newChildExecutions.Add(childExecution);
                        }
                    }
                }
            }

            return newChildExecutions;
        }

        protected bool HasSameMultiInstanceConfig(FlowElement sourceElement, FlowElement targetElement)
        {
            MultiInstanceLoopCharacteristics sourceMIConfig = null;
            if (sourceElement is Activity)
            {
                sourceMIConfig = ((Activity)sourceElement).LoopCharacteristics;
            }

            MultiInstanceLoopCharacteristics targetMIConfig = null;
            if (targetElement is Activity)
            {
                targetMIConfig = ((Activity)targetElement).LoopCharacteristics;
            }

            if (sourceMIConfig == null || targetMIConfig == null)
            {
                return false;
            }

            if (sourceMIConfig.Sequential != targetMIConfig.Sequential)
            {
                return false;
            }

            if (sourceMIConfig.LoopCardinality != null && !sourceMIConfig.LoopCardinality.Equals(targetMIConfig.LoopCardinality))
            {
                return false;
            }

            if (targetMIConfig.LoopCardinality != null && !targetMIConfig.LoopCardinality.Equals(sourceMIConfig.LoopCardinality))
            {
                return false;
            }

            return true;
        }

        protected void HandleUserTaskNewOwner(IExecutionEntity taskExecution, String newOwnerId, ICommandContext commandContext)
        {
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            ITaskService taskService = processEngineConfiguration.TaskService;
            TaskEntityImpl task = (TaskEntityImpl)taskService.CreateTaskQuery()
                    .SetExecutionId(taskExecution.Id).SingleResult();
            if (task != null)
            {
                TaskHelper.ChangeTaskOwner(task, newOwnerId, taskService);
            }
        }
        protected bool IsSubProcessAncestorOfAnyExecution(string subProcessId, List<IExecutionEntity> executions)
        {
            foreach (IExecutionEntity execution in executions)
            {
                FlowElement executionElement = execution.CurrentFlowElement;

                if (IsSubProcessAncestor(subProcessId, executionElement))
                {
                    return true;
                }
            }
            return false;
        }

        protected IExecutionEntity CreateEmbeddedSubProcessHierarchy(SubProcess subProcess, IExecutionEntity defaultParentExecution, ConcurrentDictionary<string, SubProcess> subProcessesToCreate, HashSet<string> movingExecutionIds, ProcessInstanceChangeState processInstanceChangeState, ICommandContext commandContext)
        {
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            if (processInstanceChangeState.GetProcessInstanceActiveEmbeddedExecutions().ContainsKey(subProcess.Id))
            {
                return processInstanceChangeState.GetProcessInstanceActiveEmbeddedExecutions()[subProcess.Id][0];
            }

            if (processInstanceChangeState.GetCreatedEmbeddedSubProcesses().ContainsKey(subProcess.Id))
            {
                return processInstanceChangeState.GetCreatedEmbeddedSubProcesses()[subProcess.Id];
            }

            //Create the parent, if needed
            IExecutionEntity parentSubProcess = defaultParentExecution;
            if (subProcess.SubProcess != null)
            {
                parentSubProcess = CreateEmbeddedSubProcessHierarchy(subProcess.SubProcess, defaultParentExecution, subProcessesToCreate, movingExecutionIds, processInstanceChangeState, commandContext);
                processInstanceChangeState.GetCreatedEmbeddedSubProcesses().Add(subProcess.SubProcess.Id, parentSubProcess);
            }
            IExecutionEntityManager executionEntityManager = processEngineConfiguration.ExecutionEntityManager;

            IExecutionEntity subProcessExecution = executionEntityManager.CreateChildExecution(parentSubProcess);
            subProcessExecution.CurrentFlowElement = subProcess;
            subProcessExecution.IsScope = true;

            IActivitiEventDispatcher eventDispatcher = processEngineConfiguration.EventDispatcher;
            if (eventDispatcher != null && eventDispatcher.Enabled)
            {
                eventDispatcher.DispatchEvent(
                    ActivitiEventBuilder.CreateActivityEvent(ActivitiEventType.ACTIVITY_STARTED, subProcess.Id, subProcess.Name, subProcessExecution.Id,
                        subProcessExecution.ProcessInstanceId, subProcessExecution.ProcessDefinitionId, subProcess));//,processEngineConfiguration.GetEngineCfgKey());
            }

            subProcessExecution.VariablesLocal = ProcessDataObjects(subProcess.DataObjects.ToList());

            processEngineConfiguration.HistoryManager.RecordActivityStart(subProcessExecution);

            List<BoundaryEvent> boundaryEvents = subProcess.BoundaryEvents.ToList();
            if (CollectionUtil.IsNotEmpty(boundaryEvents))
            {
                ExecuteBoundaryEvents(boundaryEvents, subProcessExecution);
            }

            if (subProcess is EventSubProcess)
            {
                throw new ActivitiException("EventSubProcess is not supported");
                //ProcessCreatedEventSubProcess((EventSubProcess)subProcess, subProcessExecution, movingExecutionIds, commandContext);
            }

            IProcessInstanceHelper processInstanceHelper = processEngineConfiguration.ProcessInstanceHelper;

            //Process containing child Event SubProcesses not contained in this creation hierarchy
            //    List<EventSubProcess> childEventSubProcesses = subProcess.FindAllSubFlowElementInFlowMapOfType(EventSubProcess.class);
            //childEventSubProcesses.stream()
            //    .filter(childEventSubProcess -> !subProcessesToCreate.containsKey(childEventSubProcess.Id))
            //    .forEach(childEventSubProcess -> processInstanceHelper.processEventSubProcess(subProcessExecution, childEventSubProcess, commandContext));
            //    var childEventSubProcesses = subProcess.FindAllSubFlowElementInFlowMapOfType(typeof(EventSubProcess))
            //.Cast<EventSubProcess>()
            //.ToList();

            //    foreach (var childEventSubProcess in childEventSubProcesses.Where(e => !subProcessesToCreate.ContainsKey(e.Id)))
            //    {
            //        processInstanceHelper.ProcessEventSubProcess(subProcessExecution, childEventSubProcess, commandContext);
            //    }
            return subProcessExecution;
        }

        protected void ExecuteBoundaryEvents(List<BoundaryEvent> boundaryEvents, IExecutionEntity execution)
        {

            // The parent execution becomes a scope, and a child execution is created for each of the boundary events
            foreach (BoundaryEvent boundaryEvent in boundaryEvents)
            {

                if (CollectionUtil.IsEmpty(boundaryEvent.EventDefinitions)
                    || (boundaryEvent.EventDefinitions[0] is CompensateEventDefinition))
                {
                    continue;
                }

                // A Child execution of the current execution is created to represent the boundary event being active
                IExecutionEntity childExecutionEntity = CommandContextUtil.GetExecutionEntityManager().CreateChildExecution(execution);
                childExecutionEntity.ParentId = execution.Id;
                childExecutionEntity.CurrentFlowElement = boundaryEvent;
                childExecutionEntity.IsScope = false;

                CommandContextUtil.GetProcessEngineConfiguration().HistoryManager.RecordActivityStart(childExecutionEntity);

                BoundaryEventActivityBehavior boundaryEventBehavior = ((BoundaryEventActivityBehavior)boundaryEvent.Behavior);
                LOGGER.LogDebug("Executing boundary event activityBehavior {} with execution {}", boundaryEventBehavior.GetType().FullName, childExecutionEntity.Id);
                boundaryEventBehavior.Execute(childExecutionEntity);
            }
        }

        protected bool IsOnlyRemainingExecutionAtParentScope(IExecutionEntity executionEntity, HashSet<string> ignoreExecutionIds, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);
            List<IExecutionEntity> siblingExecutions = executionEntityManager.FindChildExecutionsByParentExecutionId(executionEntity.ParentId).ToList();
            //return siblingExecutions.stream()
            //    .filter(ExecutionEntity::isActive)
            //    .filter(execution-> !execution.Id.Equals(executionEntity.Id))
            //    .filter(execution-> !ignoreExecutionIds.Contains(execution.Id))
            //    .count() == 0;
            return siblingExecutions
                    .Where(execution => execution.IsActive)
                    .Where(execution => execution.Id != executionEntity.Id)
                    .Where(execution => !ignoreExecutionIds.Contains(execution.Id))
                    .Count() == 0;
        }


        //protected void ProcessCreatedEventSubProcess(EventSubProcess eventSubProcess, IExecutionEntity eventSubProcessExecution, HashSet<string> movingExecutionIds, ICommandContext commandContext)
        //{
        //    ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
        //    IEventSubscriptionService eventSubscriptionService = processEngineConfiguration.GetEventSubscriptionServiceConfiguration().GetEventSubscriptionService();
        //    IExecutionEntityManager executionEntityManager = processEngineConfiguration.ExecutionEntityManager;
        //    ITimerJobService timerJobService = processEngineConfiguration.GetJobServiceConfiguration().GetTimerJobService();
        //    List<StartEvent> allStartEvents = eventSubProcess.FindAllSubFlowElementInFlowMapOfType(typeof(StartEvent));

        //    foreach (StartEvent startEvent in allStartEvents)
        //    {
        //        if (!startEvent.EventDefinitions.IsEmpty())
        //        {

        //            ICollection<IExecutionEntity> inactiveExecutionsByProcessInstanceId = executionEntityManager.FindInactiveExecutionsByProcessInstanceId(eventSubProcessExecution.ProcessInstanceId);
        //            //IExecutionEntity startEventExecution = inactiveExecutionsByProcessInstanceId.stream().filter(execution->execution.GetActivityId().equals(startEvent.Id)).findFirst();
        //            var startEventExecution = inactiveExecutionsByProcessInstanceId.FirstOrDefault(execution => execution.ActivityId == startEvent.Id);
        //            EventDefinition eventDefinition = startEvent.EventDefinitions[0];
        //            List<IEventSubscriptionEntity> eventSubscriptions = null;
        //            if (eventDefinition is SignalEventDefinition)
        //            {
        //                eventSubscriptions = eventSubscriptionService.findEventSubscriptionsByProcessInstanceAndActivityId(eventSubProcessExecution.ProcessInstanceId, startEvent.Id, SignalEventSubscriptionEntityFields.EVENT_TYPE);
        //            }
        //            else if (eventDefinition is MessageEventDefinition)
        //            {
        //                eventSubscriptions = eventSubscriptionService.findEventSubscriptionsByProcessInstanceAndActivityId(eventSubProcessExecution.ProcessInstanceId, startEvent.Id, MessageEventSubscriptionEntityFields.EVENT_TYPE);
        //            }

        //            bool isOnlyRemainingExecutionAtParentScope = IsOnlyRemainingExecutionAtParentScope(eventSubProcessExecution, movingExecutionIds, commandContext);

        //            // If its an interrupting eventSubProcess we don't register a subscription or startEvent executions and we make sure that they are removed if existed
        //            //Current eventSubProcess plus its startEvent
        //            if (startEvent.Interrupting || isOnlyRemainingExecutionAtParentScope)
        //            {
        //                if (eventSubscriptions != null && !eventSubscriptions.IsEmpty())
        //                {
        //                    //eventSubscriptions.forEach(eventSubscriptionService::deleteEventSubscription);
        //                    eventSubscriptions.ForEach(eventSubscription => eventSubscriptionService.DeleteEventSubscription(eventSubscription));

        //                }
        //                if (eventDefinition is TimerEventDefinition && startEventExecution != null)
        //                {
        //                    List<ITimerJobEntity> timerJobsByExecutionId = timerJobService.findTimerJobsByExecutionId(startEventExecution.Id);
        //                    //timerJobsByExecutionId.forEach(timerJobService::deleteTimerJob);
        //                    timerJobsByExecutionId.ForEach(id => timerJobService.DeleteTimerJob(id));
        //                }
        //                if (startEventExecution != null)
        //                {
        //                    executionEntityManager.DeleteExecutionAndRelatedData(startEventExecution, DeleteReasonFields.EVENT_SUBPROCESS_INTERRUPTING + "(" + startEvent.Id + ")", false);
        //                }

        //                //Remove any other child of the parentScope
        //                List<IExecutionEntity> childExecutions = executionEntityManager.CollectChildren(eventSubProcessExecution.Parent).ToList();
        //                for (int i = childExecutions.Count() - 1; i >= 0; i--)
        //                {
        //                    IExecutionEntity childExecutionEntity = childExecutions[i];
        //                    if (!childExecutionEntity.Ended && !childExecutionEntity.Id.Equals(eventSubProcessExecution.Id) && !movingExecutionIds.Contains(childExecutionEntity.Id))
        //                    {
        //                        executionEntityManager.DeleteExecutionAndRelatedData(childExecutionEntity,
        //                            DeleteReasonFields.EVENT_SUBPROCESS_INTERRUPTING + "(" + startEvent.Id + ")", false);
        //                    }
        //                }
        //            }
        //            else
        //            {
        //                // For non-interrupting, we register a subscription and startEvent execution if they don't exist already
        //                if (eventDefinition is MessageEventDefinition messageEventDefinition && (eventSubscriptions == null || eventSubscriptions.IsEmpty()))
        //                {
        //                    BpmnModel bpmnModel = ProcessDefinitionUtil.GetBpmnModel(eventSubProcessExecution.ProcessDefinitionId);
        //                    if (bpmnModel.ContainsMessageId(messageEventDefinition.MessageRef))
        //                    {
        //                        messageEventDefinition.MessageRef = bpmnModel.Messages.Where(p => p.Name == messageEventDefinition.MessageRef).FirstOrDefault().Name;
        //                    }

        //                    IExecutionEntity messageExecution = processEngineConfiguration.ExecutionEntityManager.CreateChildExecution(eventSubProcessExecution.Parent);
        //                    messageExecution.CurrentFlowElement = startEvent;
        //                    messageExecution.IsEventScope = true;
        //                    messageExecution.IsActive = false;

        //                    string messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, null);
        //                    IEventSubscriptionEntity messageSubscription = (IEventSubscriptionEntity)eventSubscriptionService.createEventSubscriptionBuilder()
        //                                    .eventType(MessageEventSubscriptionEntityFields.EVENT_TYPE)
        //                                    .eventName(messageName)
        //                                    .executionId(messageExecution.Id)
        //                                    .processInstanceId(messageExecution.ProcessInstanceId)
        //                                    .activityId(messageExecution.CurrentActivityId)
        //                                    .processDefinitionId(messageExecution.ProcessDefinitionId)
        //                                    .tenantId(messageExecution.TenantId)
        //                                    .create();

        //                    CountingEntityUtil.handleInsertEventSubscriptionEntityCount(messageSubscription);
        //                    messageExecution.EventSubscriptions.Add(messageSubscription);

        //                    processEngineConfiguration.EventDispatcher
        //                        .DispatchEvent(ActivitiEventBuilder.CreateMessageEvent(ActivitiEventType.ACTIVITY_MESSAGE_WAITING, messageSubscription.ActivityId,
        //                                messageSubscription.EventName, null, messageSubscription.ExecutionId,
        //                                messageSubscription.ProcessInstanceId, messageSubscription.ProcessDefinitionId));//,processEngineConfiguration.GetEngineCfgKey());

        //                }
        //                if (eventDefinition is SignalEventDefinition signalEventDefinition && (eventSubscriptions == null || eventSubscriptions.IsEmpty()))
        //                {
        //                    BpmnModel bpmnModel = ProcessDefinitionUtil.GetBpmnModel(eventSubProcessExecution.ProcessDefinitionId);
        //                    Signal signal = bpmnModel.GetSignal(signalEventDefinition.SignalRef);
        //                    if (signal != null)
        //                    {
        //                        signalEventDefinition.SignalRef = signal.Name;
        //                    }

        //                    IExecutionEntity signalExecution = processEngineConfiguration.ExecutionEntityManager.CreateChildExecution(eventSubProcessExecution.Parent);
        //                    signalExecution.CurrentFlowElement = startEvent;
        //                    signalExecution.IsEventScope = true;
        //                    signalExecution.IsActive = false;

        //                    string eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, null);

        //                    IEventSubscriptionEntity signalSubscription = (IEventSubscriptionEntity)eventSubscriptionService.createEventSubscriptionBuilder()
        //                                    .eventType(SignalEventSubscriptionEntityFields.EVENT_TYPE)
        //                                    .eventName(eventName)
        //                                    .signal(signal)
        //                                    .executionId(signalExecution.Id)
        //                                    .processInstanceId(signalExecution.ProcessInstanceId)
        //                                    .activityId(signalExecution.CurrentActivityId)
        //                                    .processDefinitionId(signalExecution.ProcessDefinitionId)
        //                                    .tenantId(signalExecution.TenantId)
        //                                    .create();

        //                    //CountingEntityUtil.handleInsertEventSubscriptionEntityCount(signalSubscription);
        //                    signalExecution.EventSubscriptions.Add(signalSubscription);

        //                    processEngineConfiguration.EventDispatcher
        //                        .DispatchEvent(ActivitiEventBuilder.CreateSignalEvent(ActivitiEventType.ACTIVITY_SIGNAL_WAITING, signalSubscription.ActivityId,
        //                                signalSubscription.EventName, null, signalSubscription.ExecutionId,
        //                                signalSubscription.ProcessInstanceId, signalSubscription.ProcessDefinitionId));//,processEngineConfiguration.GetEngineCfgKey());

        //                }

        //                if (eventDefinition is TimerEventDefinition timerEventDefinition)
        //                {
        //                    if (startEventExecution == null)
        //                    {

        //                        IExecutionEntity timerExecution = processEngineConfiguration.ExecutionEntityManager.CreateChildExecution(eventSubProcessExecution.Parent);
        //                        timerExecution.CurrentFlowElement = startEvent;
        //                        timerExecution.IsEventScope = true;
        //                        timerExecution.IsActive = false;
        //                        ITimerJobEntity timerJob = TimerUtil.CreateTimerEntityForTimerEventDefinition(timerEventDefinition, false, timerExecution, TriggerTimerEventJobHandler.TYPE,
        //                            TimerEventHandler.CreateConfiguration(startEvent.Id, timerEventDefinition.EndDate, timerEventDefinition.CalendarName));
        //                        if (timerJob != null)
        //                        {
        //                            timerJobService.scheduleTimerJob(timerJob);
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        protected void ExecuteBoundaryEvents(List<BoundaryEvent> boundaryEvents, List<IExecutionEntity> boundaryEventExecutions)
        {
            if (!CollectionUtil.IsEmpty(boundaryEventExecutions))
            {
                //Iterator<BoundaryEvent> boundaryEventsIterator = boundaryEvents.iterator();
                //Iterator<IExecutionEntity> boundaryEventExecutionsIterator = boundaryEventExecutions.iterator();
                var boundaryEventsIterator = boundaryEvents.GetEnumerator();
                var boundaryEventExecutionsIterator = boundaryEventExecutions.GetEnumerator();

                while (boundaryEventsIterator.MoveNext() && boundaryEventExecutionsIterator.MoveNext())
                {
                    BoundaryEvent boundaryEvent = boundaryEventsIterator.Current;
                    IExecutionEntity boundaryEventExecution = boundaryEventExecutionsIterator.Current;

                    BoundaryEventActivityBehavior boundaryEventBehavior = ((BoundaryEventActivityBehavior)boundaryEvent.Behavior);
                    LOGGER.LogDebug("Executing boundary event activityBehavior {} with execution {}", boundaryEventBehavior.GetType().FullName, boundaryEventExecution.Id);
                    boundaryEventBehavior.Execute(boundaryEventExecution);
                }
            }
        }
        protected bool IsEventSubProcessStart(FlowElement flowElement)
        {
            return flowElement is StartEvent && flowElement.SubProcess != null && flowElement.SubProcess is EventSubProcess;
        }

        protected void ProcessPendingEventSubProcessesStartEvents(ProcessInstanceChangeState processInstanceChangeState, ICommandContext commandContext)
        {
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            IProcessInstanceHelper processInstanceHelper = processEngineConfiguration.ProcessInstanceHelper;
            //IEventSubscriptionService eventSubscriptionService = processEngineConfiguration.EventSubscriptionServiceConfiguration.GetEventSubscriptionService();
            IManagementService managementService = processEngineConfiguration.ManagementService;

            foreach (var pendingStartEventEntry in processInstanceChangeState.GetPendingEventSubProcessesStartEvents())
            {
                StartEvent startEvent = pendingStartEventEntry.Key;
                IExecutionEntity parentExecution = pendingStartEventEntry.Value;
                EventDefinition eventDefinition = startEvent.EventDefinitions.IsEmpty() ? null : startEvent.EventDefinitions[0];

                //Process event sub process when no subscriptions/timer jobs are found
                bool processEventSubProcess = false;
                if (eventDefinition is TimerEventDefinition)
                {
                    processEventSubProcess = managementService.CreateTimerJobQuery().SetExecutionId(parentExecution.Id).List().IsEmpty();
                }
                else
                {
                    throw new ActivitiException("Unsupported event definition type " + eventDefinition.GetType().FullName);
                    //processEventSubProcess = eventSubscriptionService.findEventSubscriptionsByExecution(parentExecution.Id).IsEmpty();
                }
                if (processEventSubProcess)
                {
                    throw new ActivitiException("Unsupported event definition type " + eventDefinition.GetType().FullName);
                    //processInstanceHelper.ProcessEventSubProcess = (parentExecution, (EventSubProcess)startEvent.SubProcess, commandContext);
                }
            }
        }


        protected IExecutionEntity DeleteParentExecutions(string parentExecutionId, List<FlowElementMoveEntry> moveToFlowElements, ICommandContext commandContext)
        {
            return DeleteParentExecutions(parentExecutionId, moveToFlowElements, null, commandContext);
        }

        protected IExecutionEntity DeleteParentExecutions(string parentExecutionId, List<FlowElementMoveEntry> moveToFlowElements, List<string> executionIdsNotToDelete, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);

            IExecutionEntity parentExecution = executionEntityManager.FindById<IExecutionEntity>(parentExecutionId);
            if (parentExecution != null && parentExecution.CurrentFlowElement is SubProcess parentSubProcess)
            {
                if (!IsSubProcessAncestorOfAnyNewFlowElements(parentSubProcess.Id, moveToFlowElements))
                {
                    IExecutionEntity toDeleteParentExecution = ResolveParentExecutionToDelete(parentExecution, moveToFlowElements);
                    IExecutionEntity finalDeleteExecution = null;
                    if (toDeleteParentExecution != null)
                    {
                        finalDeleteExecution = toDeleteParentExecution;
                    }
                    else
                    {
                        finalDeleteExecution = parentExecution;
                    }

                    parentExecution = finalDeleteExecution.Parent;

                    string flowElementIdsLine = PrintFlowElementIds(moveToFlowElements);
                    executionEntityManager.DeleteChildExecutions(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
                    executionEntityManager.DeleteExecutionAndRelatedData(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
                }
            }

            return parentExecution;
        }
        protected bool IsSubProcessAncestorOfAnyNewFlowElements(string subProcessId, List<FlowElementMoveEntry> flowElements)
        {
            foreach (FlowElementMoveEntry flowElementMoveEntry in flowElements)
            {
                if (IsSubProcessAncestor(subProcessId, flowElementMoveEntry.GetNewFlowElement()))
                {
                    return true;
                }
            }
            return false;
        }

        private bool IsSubProcessAncestor(string subProcessId, FlowElement flowElement)
        {
            while (flowElement.SubProcess != null)
            {
                string execElemSubProcessId = flowElement.SubProcess.Id;
                if (execElemSubProcessId != null && execElemSubProcessId.Equals(subProcessId))
                {
                    return true;
                }
                flowElement = flowElement.SubProcess;
            }
            return false;
        }


        protected IExecutionEntity CreateCallActivityInstance(CallActivity callActivity, IProcessDefinition subProcessDefinition, IExecutionEntity parentExecution, string initialActivityId, ICommandContext commandContext)
        {

            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.GetProcessEngineConfiguration(commandContext);
            ExpressionManager expressionManager = processEngineConfiguration.ExpressionManager;
            IExecutionEntityManager executionEntityManager = processEngineConfiguration.ExecutionEntityManager;

            Process subProcess = ProcessDefinitionUtil.GetProcess(subProcessDefinition.Id);
            if (subProcess == null)
            {
                throw new ActivitiException("Cannot start a sub process instance. Process model " + subProcessDefinition.Name + " (id = " + subProcessDefinition.Id + ") could not be found");
            }

            string businessKey = null;

            if (!StringUtils.IsNullOrEmpty(callActivity.BusinessKey))
            {
                IExpression expression = expressionManager.CreateExpression(callActivity.BusinessKey);
                businessKey = expression.GetValue(parentExecution).ToString();

            }
            else if (callActivity.InheritBusinessKey)
            {
                IExecutionEntity processInstance = executionEntityManager.FindById<IExecutionEntity>(parentExecution.ProcessInstanceId);
                businessKey = processInstance.BusinessKey;
            }

            IExecutionEntity subProcessInstance = executionEntityManager.CreateSubprocessInstance(subProcessDefinition, parentExecution, businessKey);
            //if (processEngineConfiguration.EnableEntityLinks)
            //{
            //    throw new ActivitiException("Entity links are not supported for call activities");
            //    //EntityLinkUtil.createEntityLinks(parentExecution.ProcessInstanceId, parentExecution.Id, callActivity.Id,
            //    //        subProcessInstance.Id, Bpmn20NamespaceContext.BPMN);
            //}

            FlowElement initialFlowElement = subProcess.InitialFlowElement;
            if (initialFlowElement is null)
            {
                throw new ActivitiException("No start element found for process definition " + subProcessDefinition.Id);
            }
            CommandContextUtil.GetActivityInstanceEntityManager(commandContext).RecordSubProcessInstanceStart(parentExecution, subProcessInstance, initialFlowElement);

            IActivitiEventDispatcher eventDispatcher = processEngineConfiguration.EventDispatcher;
            if (eventDispatcher != null && eventDispatcher.Enabled)
            {
                CommandContextUtil.GetProcessEngineConfiguration().EventDispatcher.DispatchEvent(ActivitiEventBuilder.CreateEntityEvent(
                        ActivitiEventType.PROCESS_CREATED, subProcessInstance));//, processEngineConfiguration.GetEngineCfgKey()));
            }

            // process template-defined data objects
            subProcessInstance.Variables = ProcessDataObjects(subProcess.DataObjects.ToList());

            ConcurrentDictionary<string, object> variables = new ConcurrentDictionary<string, object>();

            if (callActivity.InheritVariables)
            {
                IDictionary<string, object> executionVariables = parentExecution.Variables;
                foreach (var entry in executionVariables)
                {
                    variables.TryAdd(entry.Key, entry.Value);
                }
            }

            // copy process variables
            //IOParameterUtil.ProcessInParameters(callActivity.InParameters, variables, expressionManager, parentExecution, null, null);

            // set local variables
            if (!variables.IsEmpty())
            {
                subProcessInstance.Variables = variables;
            }

            if (eventDispatcher != null && eventDispatcher.Enabled)
            {
                eventDispatcher.DispatchEvent(ActivitiEventBuilder.CreateEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, subProcessInstance));//,processEngineConfiguration.GetEngineCfgKey());
            }

            return subProcessInstance;
        }

        protected Dictionary<string, object> ProcessDataObjects(List<ValuedDataObject> dataObjects)
        {
            Dictionary<string, object> variablesMap = new Dictionary<string, object>();
            // convert data objects to process variables
            if (dataObjects != null)
            {
                //variablesMap = new HashMap<>(dataObjects.size());
                foreach (ValuedDataObject dataObject in dataObjects)
                {
                    variablesMap.Add(dataObject.Name, dataObject.Value);
                }
            }
            return variablesMap;
        }
        protected void SafeDeleteSubProcessInstance(string processInstanceId, List<IExecutionEntity> executionsPool, string deleteReason, ICommandContext commandContext)
        {
            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);

            //Confirm that all the subProcessExecutions are in the executions pool
            List<IExecutionEntity> subProcessExecutions = executionEntityManager.FindChildExecutionsByProcessInstanceId(processInstanceId).ToList();

            HashSet<string> executionIdsToMove = new HashSet<string>();
            foreach (IExecutionEntity executionPoolItem in executionsPool)
            {
                executionIdsToMove.Add(executionPoolItem.Id);
            }

            foreach (IExecutionEntity subProcessExecution in subProcessExecutions)
            {
                FlowElement currentFlowElement = subProcessExecution.CurrentFlowElement;
                if (currentFlowElement != null && currentFlowElement is BoundaryEvent)
                {
                    string parentExecutionId = subProcessExecution.ParentId;
                    if (!StringUtils.IsNullOrEmpty(parentExecutionId) || !executionIdsToMove.Contains(parentExecutionId))
                    {
                        throw new ActivitiException("Unbound boundary event execution prevents the sub process instance to be moved " + subProcessExecution.Id);
                    }

                }
                else if (!executionIdsToMove.Contains(subProcessExecution.Id))
                {
                    throw new ActivitiException("Following execution of sub process instance is not moved " + subProcessExecution.Id);
                }
            }

            // delete the sub process instance
            executionEntityManager.DeleteProcessInstance(processInstanceId, deleteReason, true);
        }

        protected IExecutionEntity DeleteDirectParentExecutions(string parentExecutionId, List<FlowElementMoveEntry> moveToFlowElements,
           List<string> executionIdsNotToDelete, IProcessDefinition procDefToMigrateTo,
           MoveExecutionEntityContainer moveExecutionContainer, ICommandContext commandContext)
        {

            IExecutionEntityManager executionEntityManager = CommandContextUtil.GetExecutionEntityManager(commandContext);

            IExecutionEntity parentExecution = executionEntityManager.FindById<IExecutionEntity>(parentExecutionId);
            if (parentExecution.CurrentFlowElement is SubProcess parentSubProcess)
            {
                if (!IsSubProcessContainerOfAnyFlowElement(parentSubProcess.Id, moveToFlowElements))
                {
                    if (parentSubProcess.LoopCharacteristics == null || moveExecutionContainer.GetMoveToFlowElement(parentSubProcess.Id) != null)
                    {
                        IExecutionEntity toDeleteParentExecution = ResolveParentExecutionToDelete(parentExecution, moveToFlowElements);
                        IExecutionEntity finalDeleteExecution = null;
                        if (toDeleteParentExecution != null)
                        {
                            finalDeleteExecution = toDeleteParentExecution;
                        }
                        else
                        {
                            finalDeleteExecution = parentExecution;
                        }

                        parentExecution = finalDeleteExecution.Parent;

                        string flowElementIdsLine = PrintFlowElementIds(moveToFlowElements);
                        executionEntityManager.DeleteChildExecutions(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);
                        executionEntityManager.DeleteExecutionAndRelatedData(finalDeleteExecution, "Change activity to " + flowElementIdsLine, true);

                    }
                    else
                    {
                        parentExecution.ProcessDefinitionId = procDefToMigrateTo.Id;
                        if (!parentExecution.IsMultiInstanceRoot)
                        {
                            parentExecution.Parent.ProcessDefinitionId = procDefToMigrateTo.Id;
                        }
                    }
                }
            }

            return parentExecution;
        }

        protected IExecutionEntity ResolveParentExecutionToDelete(IExecutionEntity execution, List<FlowElementMoveEntry> moveToFlowElements)
        {
            IExecutionEntity parentExecution = execution.Parent;

            if (parentExecution.ProcessInstanceType)
            {
                return null;
            }

            if (!IsSubProcessContainerOfAnyFlowElement(parentExecution.ActivityId, moveToFlowElements))
            {
                IExecutionEntity subProcessParentExecution = ResolveParentExecutionToDelete(parentExecution, moveToFlowElements);
                if (subProcessParentExecution != null)
                {
                    return subProcessParentExecution;
                }
                else
                {
                    return parentExecution;
                }
            }

            return null;
        }

        protected bool IsSubProcessContainerOfAnyFlowElement(string subProcessId, List<FlowElementMoveEntry> moveToFlowElements)
        {
            bool isUsed = moveToFlowElements
                .Select(entry => entry.GetNewFlowElement())
                .Select(element => element.SubProcess)
                .Where(subProcess => subProcess != null)
                .FirstOrDefault(subProcess => subProcess.Id == subProcessId) != null;

            return isUsed;

            //Optional<SubProcess> isUsed = moveToFlowElements.stream()
            //    .map(FlowElementMoveEntry::getNewFlowElement)
            //    .map(FlowElement::getSubProcess)
            //    .filter(Objects::nonNull)
            //    .filter(elementSubProcess->elementSubProcess.Id.equals(subProcessId))
            //    .findAny();

            //return isUsed.isPresent();
        }

        protected bool IsExecutionInsideMultiInstance(IExecutionEntity execution)
        {
            return GetFlowElementMultiInstanceParentId(execution.CurrentFlowElement) != null;
        }

        protected string GetFlowElementMultiInstanceParentId(FlowElement flowElement)
        {
            IFlowElementsContainer parentContainer = flowElement.ParentContainer;
            while (parentContainer is Activity)
            {
                if (IsFlowElementMultiInstance((Activity)parentContainer))
                {
                    return ((Activity)parentContainer).Id;
                }
                parentContainer = ((Activity)parentContainer).ParentContainer;
            }
            return null;
        }

        protected bool IsFlowElementMultiInstance(FlowElement flowElement)
        {
            if (flowElement is Activity)
            {
                return ((Activity)flowElement).LoopCharacteristics != null;
            }
            return false;
        }

        private string PrintFlowElementIds(List<FlowElementMoveEntry> flowElements)
        {
            return string.Join(",", flowElements.Select(e => e.GetNewFlowElement().Id));
        }
    }
}